/**
 * 
 */
package Dungeon;

import static org.junit.Assert.*;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.junit.Before;
import org.junit.Test;

import Border.Door;
import Border.Wall;
import ConditionPackage.Condition;
import ConditionPackage.DefaultCondition;
import SquarePackage.Direction;
import SquarePackage.NormalSquare;
import SquarePackage.Square;
import SquarePackage.TeleportableSquare;

/**
 * @author Brecht
 *
 */
public class ComposedDungeonTest {
	Dungeon<Square> dungeon1;
	ComposedDungeon<Square, Square, Subdungeon<Square>> dungeon2;
	Dungeon<TeleportableSquare> dungeon3;
	NormalSquare square1;
	NormalSquare square2;
	NormalSquare square3;
	NormalSquare square4;
	Door door;
	Wall wall;
	/**
	 * @throws java.lang.Exception
	 */
	@Before
	public void setUp() throws Exception {
		square1 = new NormalSquare();
		square2 = new NormalSquare();
		square3 = new NormalSquare();
		square4 = new NormalSquare();
		wall = new Wall(square3, Direction.NORTH);
		door = new Door(square1, Direction.WEST);
		square3.removeBorder(Direction.FLOOR);
		dungeon1 = new Level<Square>();
		dungeon2 = new ComposedDungeon<Square, Square, Subdungeon<Square>>(100,100,100);
		dungeon3 = new Level<TeleportableSquare>();
		dungeon3.addSquare(square3, new Dimension(3,6,0));
		dungeon2.addDungeon(dungeon1, new Dimension(10,10,10));
		dungeon2.addDungeon(dungeon3, new Dimension(10,10,11));
		dungeon1.addSquare(square1, new Dimension(3,6,0));
		dungeon2.addSquare(square2, (new Dimension(13,16,10)).getDimensionIn(Direction.WEST));
	}

	/**
	 * Test method for {@link Dungeon.ComposedDungeon#getDimensionOf(SquarePackage.Square)}.
	 */
	@Test
	public void testGetDimensionOf() {
		assertEquals(new Dimension(13,16,10),dungeon2.getDimensionOf(square1));
		assertEquals(new Dimension(13,16,11),dungeon2.getDimensionOf(square3));
		assertEquals(new Dimension(3,6,0),dungeon1.getDimensionOf(square1));
	}
	
	@Test
	public void testGetDungeonAt(){
		assertEquals(dungeon1, dungeon2.getSubDungeonAt(new Dimension(14,15,10)));
		assertEquals(null, dungeon2.getSubDungeonAt(new Dimension(15,12,59)));
	}

	/**
	 * Test method for {@link Dungeon.ComposedDungeon#getSquareAt(Dungeon.Dimension)}.
	 */
	@Test
	public void testGetSquareAt() {
		assertEquals(square1, dungeon2.getSquareAt(new Dimension(13,16,10)));
		assertEquals(square1, dungeon1.getSquareAt(new Dimension(3,6,0)));
	}

	/**
	 * Test method for {@link Dungeon.ComposedDungeon#removeSquareAt(Dungeon.Dimension)}.
	 */
	@Test(expected = IllegalArgumentException.class)
	public void testRemoveSquareAt() {
		dungeon2.removeSquareAt(new Dimension(13,15,10));
		fail(new Dimension(3,5,0) + " is not a valid dimension.");
	}
	
	@Test
	public void testRemoveSquareAt2() {
		dungeon2.removeSquareAt(new Dimension(13,16,11));
		assertEquals(null, dungeon3.getSquareAt(new Dimension(3,6,0)));
		assertEquals(1, dungeon1.getRoomOf(new Dimension(3,6,0)).getArea());
	}
	
	/**
	 * Test method for {@link Dungeon.ComposedDungeon#openDoor(SquarePackage.Square, SquarePackage.Direction)}.
	 */
	@Test
	public void testOpenDoor() {
		assertEquals(2,dungeon2.getRoomOf(square1).getArea());
		assertEquals(1,dungeon2.getRoomOf(square2).getArea());
		assertEquals(2,dungeon2.getRoomOf(square3).getArea());
		dungeon2.openDoor(square1, Direction.WEST);
		assertEquals(3,dungeon2.getRoomOf(square1).getArea());
		assertEquals(3,dungeon2.getRoomOf(square2).getArea());
		assertEquals(3,dungeon2.getRoomOf(square3).getArea());
		assertTrue(square1.getTemp()==square2.getTemp());
		assertTrue(square1.getTemp()==square3.getTemp());
		assertEquals(square1.getTemp(),dungeon2.getRoomOf(square1).getTemperature().getTemperature(),0.5);
		assertTrue(square1.getHumidity()==square2.getHumidity());
		assertTrue(square1.getHumidity()==square3.getHumidity());
		assertEquals(square1.getHumidity(),dungeon2.getRoomOf(square1).getHumidity());
	}
	
	/**
	 * Test method for {@link Dungeon.ComposedDungeon#closeDoor(SquarePackage.Square, SquarePackage.Direction)}.
	 */
	@Test
	public void testCloseDoor() {
		dungeon2.openDoor(square1, Direction.WEST);
		assertEquals(3,dungeon2.getRoomOf(square1).getArea());
		assertEquals(3,dungeon2.getRoomOf(square2).getArea());
		assertEquals(3,dungeon2.getRoomOf(square3).getArea());
		dungeon1.closeDoor(square2, Direction.EAST);
		assertEquals(2,dungeon2.getRoomOf(square1).getArea());
		assertEquals(1,dungeon2.getRoomOf(square2).getArea());
		assertEquals(2,dungeon2.getRoomOf(square3).getArea());
	}

	/**
	 * Test method for {@link Dungeon.ComposedDungeon#hasBeenUsed(SquarePackage.Square)}.
	 */
	@Test
	public void testHasBeenUsed() {
		assertTrue(dungeon1.hasBeenUsed(square1));
		assertTrue(dungeon2.hasBeenUsed(square1));
		assertTrue(dungeon3.hasBeenUsed(square1));
		assertFalse(dungeon1.hasBeenUsed(square4));
		assertFalse(dungeon2.hasBeenUsed(square4));
		assertFalse(dungeon3.hasBeenUsed(square4));
	}

	/**
	 * Test method for {@link Dungeon.ComposedDungeon#addSquare(SquarePackage.Square, Dungeon.Dimension)}.
	 */
	@Test(expected = IllegalArgumentException.class)
	public void testAddSquare() {
		dungeon2.addSquare(square4, new Dimension(13,72,8));
		fail();
	}

	/**
	 * Test method for {@link Dungeon.ComposedDungeon#getUsedDimensions()}.
	 */
	@Test
	public void testGetUsedDimensions() {
		assertEquals(3, dungeon2.getUsedDimensions().size());
	}

	/**
	 * Test method for {@link Dungeon.ComposedDungeon#getDungeons()}.
	 */
	@Test
	public void testGetDungeons() {
		Map<Dimension, Subdungeon<? extends Square>> map = new HashMap<Dimension, Subdungeon<? extends Square>>();
		map.put(new Dimension(10,10,10), dungeon1);
		map.put(new Dimension(10,10,11), dungeon3);
		assertEquals(2,dungeon2.getDungeons().size());
	}

	/**
	 * Test method for {@link Dungeon.ComposedDungeon#getDimensions()}.
	 */
	@Test
	public void testGetDimensions() {
		Set<Dimension> set = new HashSet<Dimension>();
		set.add(new Dimension(10,10,10));
		set.add(new Dimension(10,10,11));
		assertEquals(set, dungeon2.getDimensions());
	}

	/**
	 * Test method for {@link Dungeon.ComposedDungeon#getAllDungeons()}.
	 */
	@Test
	public void testGetAllSubdungeons() {
		assertEquals(2, dungeon2.getAllSubdungeons().size());
	}

	/**
	 * Test method for {@link Dungeon.ComposedDungeon#addDungeon(Dungeon.Subdungeon, Dungeon.Dimension)}.
	 */
	@Test
	public void testAddDungeon() {
		Dungeon<NormalSquare> dungeon4 = new Level<NormalSquare>();
		dungeon2.addDungeon(dungeon4, new Dimension(18,36,18));
	}

	/**
	 * Test method for {@link Dungeon.ComposedDungeon#removeDungeon(Dungeon.Subdungeon)}.
	 */
	@Test
	public void testRemoveDungeon() {
		dungeon2.removeDungeon(dungeon3);
		assertEquals(1,dungeon2.getDungeons().size());
		assertEquals(1,dungeon2.getRoomOf(square1).getArea());
	}

	/**
	 * Test method for {@link Dungeon.ComposedDungeon#getRootDimension(Dungeon.Dimension, Dungeon.Subdungeon)}.
	 */
	@Test
	public void testGetRootDimension() {
		assertEquals(new Dimension(16,15,17), dungeon2.getRootDimension(new Dimension(16,15,17)));
		assertEquals(new Dimension(13,15,26), dungeon1.getRootDimension(new Dimension(3,5,16)));
	}

	/**
	 * Test method for {@link Dungeon.Subdungeon#getMaxDimension()}.
	 */
	@Test
	public void testGetMaxDimension() {
		assertEquals(new Dimension(100,100,100),dungeon2.getMaxDimension());
	}

	/**
	 * Test method for {@link Dungeon.Subdungeon#setMaxDimension(Dungeon.Dimension)}.
	 */
	@Test
	public void testSetMaxDimension() {
		dungeon1.setMaxDimension(new Dimension(30,30,0));
		assertEquals(new Dimension(30,30,0),dungeon1.getMaxDimension());
	}

	/**
	 * Test method for {@link Dungeon.Subdungeon#isValidNewMaxDimension(Dungeon.Dimension)}.
	 */
	@Test
	public void testIsValidNewMaxDimension() {
		assertFalse(dungeon1.isValidNewMaxDimension(new Dimension(20,20,2)));
		assertFalse(dungeon1.isValidNewMaxDimension(new Dimension(19,20,0)));
		assertFalse(dungeon1.isValidNewMaxDimension(new Dimension(1000, 20, 0)));
		assertTrue(dungeon1.isValidNewMaxDimension(new Dimension(30,30,0)));
	}

	/**
	 * Test method for {@link Dungeon.Subdungeon#getRootDungeon()}.
	 */
	@Test
	public void testGetRootDungeon() {
		assertEquals(dungeon2, dungeon1.getRootDungeon());
		assertEquals(dungeon2, dungeon2.getRootDungeon());
	}

	/**
	 * Test method for {@link Dungeon.Subdungeon#getContainingDungeon()}.
	 */
	@Test
	public void testGetContainingDungeon() {
		assertEquals(dungeon2, dungeon3.getContainingDungeon());
		assertEquals(dungeon2, dungeon1.getContainingDungeon());
	}

	/**
	 * Test method for {@link Dungeon.Subdungeon#hasContainingDungeon()}.
	 */
	@Test
	public void testHasContainingDungeon() {
		assertTrue(dungeon1.hasContainingDungeon());
		assertFalse(dungeon2.hasContainingDungeon());
		assertTrue(dungeon3.hasContainingDungeon());
	}

	/**
	 * Test method for {@link Dungeon.Subdungeon#isValidDimension(Dungeon.Dimension)}.
	 */
	@Test
	public void testIsValidDimension() {
		assertFalse(dungeon2.isValidDimension(new Dimension(13,16,10)));
		assertFalse(dungeon2.isValidDimension(new Dimension(15,15,15)));
		assertFalse(dungeon3.isValidDimension(new Dimension(1,1,0)));
		assertFalse(dungeon1.isValidDimension(new Dimension(15,16,18)));
		assertFalse(dungeon2.isValidDimension(new Dimension(15,16,18)));
		assertTrue(dungeon2.isValidDimension(new Dimension(13,18,10)));
	}

	/**
	 * Test method for {@link Dungeon.Subdungeon#isAvailable(Dungeon.Dimension)}.
	 */
	@Test
	public void testIsAvailable() {
		assertFalse(dungeon2.isAvailable(new Dimension(13,16,10)));
		assertFalse(dungeon3.isAvailable(new Dimension(3,6,0)));
		assertTrue(dungeon2.isAvailable(new Dimension(100,50,23)));
		assertTrue(dungeon1.isAvailable(new Dimension(3,12,0)));
	}

	/**
	 * Test method for {@link Dungeon.Subdungeon#isValidSquare(SquarePackage.Square)}.
	 */
	@Test
	public void testIsValidSquare() {
		assertTrue(dungeon2.isValidSquare(square4));
		assertTrue(dungeon3.isValidSquare(square4));
		assertTrue(dungeon1.isValidSquare(square4));
		assertFalse(dungeon1.isValidSquare(square1));
		assertFalse(dungeon2.isValidSquare(square1));
		assertFalse(dungeon3.isValidSquare(square1));
	}

	/**
	 * Test method for {@link Dungeon.Subdungeon#getSlipperyPercent()}.
	 */
	@Test
	public void testGetSlipperyPercent() {
		Condition condition = new DefaultCondition();
		Iterator<? extends Square> it = dungeon2.iterator(condition);
		int i = 0;
		int total = 0;
		while(it.hasNext()){
			if(it.next().isSlippery())
				i++;
			total++;
		}
		int a = (100*i)/total;
		assertEquals(a,dungeon2.getSlipperyPercent());
	}

}
